// src/plugins/axios.ts
import http from '@/config/http'
import { Aes } from '@/utils/aes'
import qs from 'qs'
import md5 from 'js-md5'
import { showToast, ToastType } from 'vant'
import type { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'

const aes = new Aes()

function setNonce(len: number = 32): string {
  const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
  let pwd = ''
  for (let i = 0; i < len; i++) {
    pwd += $chars.charAt(Math.floor(Math.random() * $chars.length))
  }
  return pwd
}

const generateReqKey = (config: AxiosRequestConfig, hash: string): string => {
  const { method, url, params, data } = config
  return [method, url, JSON.stringify(params), JSON.stringify(data), hash].join('&')
}
const pendingRequest = new Set<string>()

// 扩展 AxiosRequestConfig 以支持自定义字段
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
  allowDuplicate?: boolean
  pendKey?: string
}

// 请求拦截
http.interceptors.request.use(
  (config: InternalAxiosRequestConfig & CustomAxiosRequestConfig) => {
    const token = localStorage.getItem(`${import.meta.env.MODE}_token`) || ''
    const nonce = setNonce(32)
    const timestamp = Date.now()
    const key = 'rh5ffurhv28m2q14'
    config.headers = config.headers || {}
    config.headers.Nonce = nonce
    config.headers.Timestamp = timestamp
    if (token) {
      const param = `Authorization=Bearer ${token}&Nonce=${nonce}&Timestamp=${timestamp}&Key=${key}`
      config.headers.Sign = md5(param).toUpperCase()
      config.headers.Authorization = `Bearer ${token}`
    } else {
      const param = `Nonce=${nonce}&Timestamp=${timestamp}&Key=${key}`
      config.headers.Sign = md5(param).toUpperCase()
    }
    config.headers.clientType = import.meta.env.VITE_HOME_NAME === 'PRICOO' ? 'PRICOO_PC' : 'HAIER_PC'
    if (!config.allowDuplicate) {
      let hash = location.hash
      const reqKey = generateReqKey(config, hash)
      if (pendingRequest.has(reqKey)) {
        return Promise.reject(new Error('请求重复'))
      }
      config.pendKey = reqKey
      pendingRequest.add(reqKey)
    }
    return config
  },
  error => Promise.reject(error)
)

// 响应拦截
http.interceptors.response.use(
  (response: AxiosResponse) => {
    const config = response.config as CustomAxiosRequestConfig
    if (config.pendKey) {
      pendingRequest.delete(config.pendKey)
    }
    let deResponse: any = []
    if (response.data && typeof response.data === 'string' && response.data.length > 1) {
      deResponse = JSON.parse(aes.doDecrypt(response.data))
    } else {
      deResponse = response.data
    }
    if ([200, 201, 204].includes(response.status)) {
      return Promise.resolve({ data: deResponse, headers: response.headers })
    }
    return Promise.reject(response)
  },
  (error: any) => {
    const config = error.config as CustomAxiosRequestConfig
    if (config && config.pendKey) {
      pendingRequest.delete(config.pendKey)
    }
    if (error?.response?.status) {
      switch (error.response.status) {
        case 401:
          if (error.response.data.error === 'invalid_token') {
            localStorage.clear()
          }
          window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname)
          break
        case 403:
          localStorage.clear()
          localStorage.removeItem('userInfo')
          localStorage.removeItem(`${import.meta.env.MODE}_token`)
          setTimeout(() => {
            window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname)
            if ([912, 913].includes(error.response?.data?.code)) {
              openVantToast(error.response?.data?.message, 'fail', true)
            }
          }, 1000)
          break
        case 404:
          break
        default:
      }
      return Promise.reject(error.response)
    }
    return Promise.reject(error)
  }
)

const openVantToast = (msg: string, type: ToastType = 'text', isRestrict = false) => {
  if (isRestrict && (window as any).__toastVisible) return
  (window as any).__toastVisible = true
  showToast({
    message: msg,
    type,
    onClose: () => {
      (window as any).__toastVisible = false
    }
  })
}

// 通用请求方法
const post = (url: string, data: any, options?: { isDoEncrypt?: boolean }) => {
  const doEncrypt = options?.isDoEncrypt ? false : true
  let deData = typeof data === 'string' ? data : JSON.stringify(data)
  const parms = doEncrypt ? aes.doEncrypt(deData) : deData
  return http.post(url, parms, {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
      accept: '*/*',
      zongVersion: 1
    }
  })
}

const post_form = (url: string, data: Record<string, any>) => {
  return http.post(url, qs.stringify(data), {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
      accept: '*/*',
      zongVersion: 1
    }
  })
}

const get = (url: string, data: Record<string, any> = {}, headers?: Record<string, any>) => {
  let params: Record<string, any> = {}
  for (let key in data) {
    let value = data[key]
    if (typeof value === 'object') value = JSON.stringify(value)
    params[key] = value
  }
  let getUrl = JSON.stringify(params) == '{}' ? url : url + '?' + qs.stringify(params)
  return http.get(getUrl, {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
      'Accept': '*/*',
      zongVersion: 1
    }
  })
}

const del = (url: string, data: any) => {
  let deData = typeof data === 'string' ? data : JSON.stringify(data)
  return http.delete(url, {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8'
    },
    data: aes.doEncrypt(deData)
  })
}

const put = (url: string, data: any) => {
  let deData = typeof data === 'string' ? data : JSON.stringify(data)
  return http.put(url, aes.doEncrypt(deData), {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8'
    }
  })
}

const patch = (url: string, data: any) => {
  let deData = typeof data === 'string' ? data : JSON.stringify(data)
  return http.patch(url, aes.doEncrypt(deData), {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
      accept: '*/*'
    }
  })
}

const post_file = (url: string, data: any, config: { allowDuplicate?: boolean } = {}) => {
  return http.post(url, data, {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
      accept: '*/*',
      zongVersion: 1
    },
    // @ts-ignore
    allowDuplicate: config.allowDuplicate || false
  })
}

export default {
  post,
  get,
  patch,
  del,
  put,
  post_form,
  post_file
}